package cn.pugle.base.thread.forkjoin;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

/**
 * 另见 LC23_1 FJ多路归并
 *
 * @author tzp
 * @since 2020/10/27
 */
public class FJCounter {
    static class Counter2 extends ForkJoinTask<Integer> {
        @Override
        public Integer getRawResult() {
            return null;
        }

        @Override
        protected void setRawResult(Integer value) {

        }

        @Override
        protected boolean exec() {
            return false;
        }
    }

    /**
     * 找到数组中大于50的元素的个数;
     * 长度<=10的时候直接运算, 否则fork
     */
    static class Counter1 extends RecursiveTask<Integer> {
        static int maxComputeLength = 10;
        int[] ar;
        int start;
        int end;

        public Counter1(int[] ar, int start, int end/*exclude*/) {
            this.ar = ar;
            this.start = start;
            this.end = end;
        }

        @Override
        protected Integer compute() {
            String tname = Thread.currentThread().getName();
            if (end - start <= maxComputeLength) {
                System.out.println(tname + "开始实际计算");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
                int cnt = 0;
                for (int i = start; i < end; i++) {
                    if (ar[i] > 50) cnt++;
                }
                System.out.println(tname + "结束实际计算");
                return cnt;
            } else {
                //直接分够得了, 不写二分再二分了
                System.out.println(tname + "开始fork");
                int segmentStart = start;
                int segmentEnd = start + maxComputeLength;
                List<Counter1> tasks = new ArrayList<>();
                while (segmentStart < end) {
                    Counter1 subTask = new Counter1(ar, segmentStart, Math.min(segmentEnd, end));
                    subTask.fork();
                    tasks.add(subTask);
                    segmentStart += maxComputeLength;
                    segmentEnd += maxComputeLength;
                }
                System.out.println(tname + "等待fork结果");
                //invokeAll(tasks);//相当于挨个fork, 挨个join
                int sum = 0;
                for (Counter1 task : tasks) {
                    Integer join = task.join();
                    sum += join;
                }
                System.out.println(tname + "汇总返回结果");
                return sum;
            }
        }

    }

    public static void main(String[] args) {
        int[] ar = new int[39];
        Random rand = new Random();
        for (int i = 0; i < 39; i++) {
            ar[i] = rand.nextInt(100);
        }

//        {
//            Counter1 task = new Counter1(ar, 0, 39);
//            Integer compute = task.compute();//直接调用的话, 相当于用当前main线程做的事, 里面的fork会放到common, pool里
//            System.out.println(compute);
//            System.out.println("-------------");
//        }
//        {
//            //FIXME 卧槽, 这种调用方式很诡异的, 大概率输出的是main线程"开始fork" 小概率输出"ForkJoinPool.commonPool-worker-1开始fork"
//            Counter1 task = new Counter1(ar, 0, 39);
//            task.fork();
//            Integer join = task.join();
//            System.out.println(join);
//            System.out.println("-------------");
//            //FIXME 以上都不是正确的调用方式
//        }
//        {
//            //FIXME 卧槽这种调用也是会出现main开始fork
//            Counter1 task = new Counter1(ar, 0, 39);
//            ForkJoinPool commonPool = ForkJoinPool.commonPool();
//            commonPool.submit(task);
//            try {
//                Integer integer = task.get();
//                System.out.println(integer);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            } catch (ExecutionException e) {
//                e.printStackTrace();
//            }
//            System.out.println("-------------");
//        }
//        {
//            Counter1 task = new Counter1(ar, 0, 39);
//            ForkJoinPool commonPool = ForkJoinPool.commonPool();
//            commonPool.submit(task);
//            try {
//                Thread.sleep(4000);//加入这行, 就不会出现main帮忙干活的情况了
//                Integer integer = task.get();
//                System.out.println(integer);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            } catch (ExecutionException e) {
//                e.printStackTrace();
//            }
//            System.out.println("-------------");
//        }

        {
            Counter1 task = new Counter1(ar, 0, 39);
            ForkJoinPool newPool = (ForkJoinPool) Executors.newWorkStealingPool(1);
            //FIXME 池子只有1个线程也能出结果, 说明task执行join的时候让出了当前线程?? 怎么做到的??
            //池子大的时候, 不等第一个task执行join, 他的子任务也已经开始执行了
            newPool.submit(task);
//            newPool.execute(task);
            try {
                Thread.sleep(4000);//加入这行, 就不会出现main帮忙干活的情况了
                Integer integer = task.get();
                System.out.println(integer);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            System.out.println("-------------");
        }
    }
}
